package top.shaozuo.simple.configuration.microprofile.config;

import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.stream.Collectors;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;

import top.shaozuo.simple.configuration.microprofile.config.converts.BooleanConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.ByteConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.DoubleConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.FloatConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.IntegerConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.LongConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.ShortConverter;
import top.shaozuo.simple.configuration.microprofile.config.converts.StringConverter;

public class JavaConfig implements Config {

    /**
     * 内部可变的集合，不要直接暴露在外面
     */
    private List<ConfigSource> configSources = new LinkedList<>();
    private Map<Type, Converter> converters = new HashMap<>();

    private static Comparator<ConfigSource> configSourceComparator = new Comparator<ConfigSource>() {
        @Override
        public int compare(ConfigSource o1, ConfigSource o2) {
            return Integer.compare(o2.getOrdinal(), o1.getOrdinal());
        }
    };

    public JavaConfig() {
        ClassLoader classLoader = getClass().getClassLoader();
        ServiceLoader<ConfigSource> serviceLoader = ServiceLoader.load(ConfigSource.class,
                classLoader);
        serviceLoader.forEach(configSources::add);
        // 排序
        configSources.sort(configSourceComparator);
        registerDefaultConverter();
    }

    private void registerDefaultConverter() {
        converters.put(Boolean.class, BooleanConverter.INSTANCE);
        converters.put(boolean.class, BooleanConverter.INSTANCE);
        converters.put(Byte.class, ByteConverter.INSTANCE);
        converters.put(byte.class, ByteConverter.INSTANCE);
        converters.put(Short.class, ShortConverter.INSTANCE);
        converters.put(short.class, ShortConverter.INSTANCE);
        converters.put(Integer.class, IntegerConverter.INSTANCE);
        converters.put(int.class, IntegerConverter.INSTANCE);
        converters.put(Long.class, LongConverter.INSTANCE);
        converters.put(long.class, LongConverter.INSTANCE);
        converters.put(Float.class, FloatConverter.INSTANCE);
        converters.put(float.class, FloatConverter.INSTANCE);
        converters.put(Double.class, DoubleConverter.INSTANCE);
        converters.put(double.class, DoubleConverter.INSTANCE);
        converters.put(String.class, StringConverter.INSTANCE);

    }

    @Override
    public <T> T getValue(String propertyName, Class<T> propertyType) {
        String propertyValue = getPropertyValue(propertyName);
        // String 转换成目标类型
        if (propertyValue != null) {
            Optional<Converter<T>> convertHolder = getConverter(propertyType);
            if (!convertHolder.isPresent()) {
                throw new IllegalArgumentException(
                        "No Converter registered for class " + propertyType);
            }
            Converter<T> converter = convertHolder.get();
            return converter.convert(propertyValue);
        }
        return null;
    }

    @Override
    public ConfigValue getConfigValue(String propertyName) {
        return null;
    }

    protected String getPropertyValue(String propertyName) {
        String propertyValue = null;
        for (ConfigSource configSource : configSources) {
            propertyValue = configSource.getValue(propertyName);
            if (propertyValue != null) {
                break;
            }
        }
        return propertyValue;
    }

    @Override
    public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {
        T value = getValue(propertyName, propertyType);
        return Optional.ofNullable(value);
    }

    @Override
    public Iterable<String> getPropertyNames() {
        return configSources.stream().flatMap(c -> c.getPropertyNames().stream())
                .collect(Collectors.toSet());
    }

    @Override
    public Iterable<ConfigSource> getConfigSources() {
        return Collections.unmodifiableList(configSources);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> Optional<Converter<T>> getConverter(Class<T> forType) {
        return Optional.of(converters.get(forType));
    }

    @Override
    public <T> T unwrap(Class<T> type) {
        return null;
    }
}
